import { ElectronService } from 'ngx-electron'
import { Injectable, NgZone } from '@angular/core'
import { Observable, Subscriber } from 'rxjs'
import { nanoid } from 'nanoid'
import { asyncChannels } from '../../../app/asyncChannels'
import IpcRenderer = Electron.IpcRenderer
import { map } from 'rxjs/operators'

@Injectable({
    providedIn: 'root'
})
export class IpcClient {
    private ipcRenderer: IpcRenderer

    constructor(electron: ElectronService, private zone: NgZone) {
        this.ipcRenderer = electron.ipcRenderer
    }

    public sendAsync<T>(channel, ...args): Observable<T> {
        const uid = nanoid()
        const replyChannel = channel + '-reply'
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, ...args)
        })
    }

    public openDirectory() {
        const opts: OpenDialogOptions = {properties: ['openDirectory']}
        return this.sendAsync<OpenDialogReturnValue>(asyncChannels.showOpenDialog, opts)
    }

    public openFile() {
        const opts: OpenDialogOptions = {properties: ['openFile']}
        return this.sendAsync<OpenDialogReturnValue>(asyncChannels.showOpenDialog, opts)
    }

    private listenOne<T>(relayChannel: string, uid: string, subscriber: Subscriber<T>) {
        const timeoutHandler = setTimeout(() => {
            console.warn('请求时间过长')
        }, 10000)
        let listener
        this.ipcRenderer.on(relayChannel, listener = async (event, rid, [resp, err]) => {
            if (rid === uid) {
                this.ipcRenderer.removeListener(relayChannel, listener)
                clearTimeout(timeoutHandler)
                this.zone.run(() => {
                    err ? subscriber.error(err) : subscriber.next(resp)
                    subscriber.complete()
                })
            }
        })
    }
}

export interface FileFilter {
    extensions: string[];
    name: string;
}

export interface OpenDialogOptions {
    title?: string;
    defaultPath?: string;
    /**
     * Custom label for the confirmation button, when left empty the default label will be used.
     */
    buttonLabel?: string;
    filters?: FileFilter[];
    /**
     * Contains which features the dialog should use. The following values are supported:
     */
    properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory' | 'dontAddToRecent'>;
    /**
     * Message to display above input boxes.
     */
    message?: string;
    /**
     * Create security scoped bookmarks when packaged for the Mac App Store.
     */
    securityScopedBookmarks?: boolean;
}

interface OpenDialogReturnValue {
    /**
     * whether or not the dialog was canceled.
     */
    canceled: boolean;
    /**
     * An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array.
     */
    filePaths: string[];
    /**
     * An array matching the `filePaths` array of base64 encoded strings which contains
     * security scoped bookmark data. `securityScopedBookmarks` must be enabled for
     * this to be populated. (For return values, see table here.)
     */
    bookmarks?: string[];
}
